package com.example.gobang_system.service.match;

import com.example.gobang_system.common.pojo.CommonResult;
import com.example.gobang_system.common.pojo.UserInfo;
import com.example.gobang_system.common.util.JacksonUtil;
import com.example.gobang_system.service.enums.MatchStatusEnum;
import com.example.gobang_system.service.manager.OnlineUserManager;
import com.example.gobang_system.common.pojo.Room;
import com.example.gobang_system.service.manager.RoomManager;
import com.example.gobang_system.websockethandler.result.MatchResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 匹配器
 */
@Component
@Slf4j
public class Matcher {
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private RoomManager roomManager;
    public static final int HIGH_SCORE = 2000;
    public static final int VERY_HIGH_SCORE = 3000;

    // 创建三个队列，分别用于匹配不同天梯分数的玩家
    private Queue<UserInfo> normalQueue = new LinkedList<>(); // score < 2000
    private Queue<UserInfo> highQueue = new LinkedList<>(); // 2000 <= score < 3000
    private Queue<UserInfo> veryHighQueue = new LinkedList<>(); // 3000 <= score

    /**
     * 将玩家放入匹配队列
     * @param userInfo
     */
    public void addUserInfo(UserInfo userInfo) {
        // 参数校验
        if (null == userInfo) {
            return;
        }
        // 放入对应队列进行匹配
        if (userInfo.getScore() < HIGH_SCORE) {
            offer(normalQueue, userInfo);
        } else if (userInfo.getUserId() >= HIGH_SCORE
                && userInfo.getUserId() < VERY_HIGH_SCORE) {
            offer(highQueue, userInfo);
        } else {
            offer(veryHighQueue, userInfo);
        }
    }

    /**
     * 将玩家从匹配队列中移除
     * @param userInfo
     */
    public void removeUserInfo(UserInfo userInfo) {
        // 参数校验
        if (null == userInfo) {
            return;
        }
        // 从对应队列中移除玩家
        if (userInfo.getScore() < HIGH_SCORE) {
            remove(normalQueue, userInfo);
        } else if (userInfo.getUserId() >= HIGH_SCORE
                && userInfo.getUserId() < VERY_HIGH_SCORE) {
            remove(highQueue, userInfo);
        } else {
            remove(veryHighQueue, userInfo);
        }
    }

    /**
     * 将玩家放入匹配队列中
     * @param queue
     * @param userInfo
     */
    private void offer(Queue<UserInfo> queue, UserInfo userInfo) {
        try {
            synchronized (queue) {
                // 将玩家添加到队列中
                queue.offer(userInfo);
                // 唤醒线程进行匹配
                queue.notify();
            }
            log.info("玩家 {} 进入匹配队列 {}", userInfo.getUserName(), queue.getClass().getName());
        } catch (Exception e) {
            log.warn("向队列 {} 中添加玩家 {} 异常, e: ", queue.getClass().getName(),
                    userInfo.getUserName(), e);
        }
    }


    /**
     * 从队列中移除玩家信息
     * @param queue
     * @param userInfo
     */
    private void remove (Queue<UserInfo> queue, UserInfo userInfo) {
        try {
            synchronized (queue) {
                // 将玩家从队列中移除
                queue.remove(userInfo);
            }
            log.info("玩家 {} 从匹配队列中退出", userInfo.getUserName());
        } catch (Exception e) {
            log.warn("从匹配队列中移除玩家 {} 异常, e: ", userInfo.getUserName(), e);
        }
    }

    public Matcher() {
        // 创建扫描线程，进行匹配
        Thread normalThread = new Thread(() -> {
            while (true) {
                handlerMatch(normalQueue);
            }
        });

        Thread highThread = new Thread(() -> {
            while (true) {
                handlerMatch(highQueue);
            }
        });

        Thread veryHighThread = new Thread(() -> {
            while (true) {
                handlerMatch(veryHighQueue);
            }
        });

        // 启动线程
        normalThread.start();
        highThread.start();
        veryHighThread.start();

    }

    /**
     * 进行匹配
     * @param queue
     */
    private void handlerMatch(Queue<UserInfo> queue) {
        try {
            synchronized (queue) {
                // 若队列中为空 或 队列中只有一个玩家信息，阻塞等待
                while (queue.size() <= 1) {
                    queue.wait();
                }
                // 取出两个玩家进行匹配
                UserInfo user1 = queue.poll();
                UserInfo user2 = queue.poll();
                // 通知两个玩家匹配到对手
                WebSocketSession session1 = onlineUserManager.getFromHall(user1.getUserId());
                WebSocketSession session2 = onlineUserManager.getFromHall(user2.getUserId());
                // 判断两个玩家当前是否都在线
                if (null == session1 && null == session2) {
                    // 两个玩家都已下线
                    return;
                }
                if (null == session1) {
                    // 玩家1 已下线, 将 玩家2 重新放回匹配队列
                    queue.offer(user2);
                    log.info("玩家 {} 重新进行匹配", user2.getUserName());
                    return;
                }
                if (null == session2) {
                    // 玩家2 已下线, 将 玩家1 重新放回匹配队列
                    queue.offer(user1);
                    log.info("玩家 {} 重新进行匹配", user1.getUserName());
                    return;
                }
                if (session1 == session2) {
                    // 两个 session 相同，同一个玩家两次进入匹配队列
                    queue.offer(user1);
                    return;
                }
                // 为上述匹配成功的两个玩家创建游戏房间
                Room room = new Room();
                roomManager.add(room, user1.getUserId(), user2.getUserId());

                // 通知玩家匹配成功
                session1.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(
                        CommonResult.success(convertToMatchSuccessResult(user2)))));
                session2.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(
                        CommonResult.success(convertToMatchSuccessResult(user1)))));
            }
        } catch (InterruptedException e) {
            log.warn("Matcher 被提前唤醒", e);
        } catch (Exception e) {
            log.error("Matcher 处理异常", e);
        }
    }

    /**
     * 构造 MatchResult
     * @param rivalInfo
     * @return
     */
    private MatchResult convertToMatchSuccessResult(UserInfo rivalInfo) {
        // 参数校验
        if (null == rivalInfo) {
            return null;
        }
        // 构造 MatchResult
        MatchResult.Rival rival = new MatchResult.Rival();
        rival.setName(rivalInfo.getUserName());
        rival.setScore(rivalInfo.getScore());
        MatchResult result = new MatchResult();
        result.setMatchMessage(MatchStatusEnum.SUCCESS.name());
        result.setRival(rival);
        // 返回
        return result;
    }
}
